#include "th.h"


#ifdef __linux__
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
C_CODE_BEGIN
EXPORT_FUNC bool thread_create( thread_handler* handler , const char* name , thread_cb cb , void* par  , bool detach ) {
	/* on linux version , the name parameter is ignored by default */
	pthread_attr_t attr;
	int ret;
	if( detach == true ) {
		// since the default thread is joinable , here we should use attr to set the detach
		ret = pthread_attr_init(&attr);
		assert( ret == 0 );
		ret = pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
		assert( ret == 0 );
		/*
		 * create the pthread here
		 */
		 ret = pthread_create(handler,&attr,cb,par);
		 pthread_attr_destroy(&attr);
		 return ret == 0 ? true : false;
	}
	
	return pthread_create(handler,NULL,cb,par) == 0 ? true : false;
}


EXPORT_FUNC bool thread_join( thread_handler handler , void** return_val , const struct th_join_sec* ti ) {
	struct 	timespec spec;
	if( ti == NULL ) {
		return pthread_join(handler, return_val ) == 0 ? true : false;
	} else {
		if( ti->sec == 0 && ti->micro_sec ==0 ) {
			return pthread_tryjoin_np(handler,return_val) == 0 ? true : false;
		} else {
			spec.tv_nsec = ti->micro_sec*1000;
			spec.tv_sec = ti->sec;
			return pthread_timedjoin_np(handler,return_val,&spec) == 0  ? true :false;
		}
	}
}

EXPORT_FUNC thread_handler thread_current() {
	return syscall(__NR_gettid);
} 

EXPORT_FUNC void thread_yield() {
	pthread_yield();
}


EXPORT_FUNC void thread_sleep( const struct th_join_sec* sec ) {
	if( sec == NULL )
		thread_yield();
	usleep( sec->micro_sec + sec->sec*1000000u );
}
	
EXPORT_FUNC void thread_fastlock_create( fastlock* fl ) {
	int ret;
#ifndef NDEBUG
	int mret;
	pthread_mutexattr_t attr;
	ret = pthread_mutexattr_init( &attr );
	assert( ret == 0 );
	ret = pthread_mutexattr_settype( & attr , PTHREAD_MUTEX_ERRORCHECK );
	assert( ret == 0 );
	mret = pthread_mutex_init(fl,&attr);
	ret = pthread_mutexattr_destroy(&attr);
	assert( ret == 0 && mret == 0 );
#else
	ret = pthread_mutex_init(fl,NULL);
	assert(ret == 0);
#endif
}

EXPORT_FUNC void thread_fastlock_destroy( fastlock *fl ) {
	int ret = pthread_mutex_destroy(fl);
	assert( ret == 0 );
}

EXPORT_FUNC bool thread_fastlock_require( fastlock* fl , const struct th_join_sec* ti ) {
	struct timespec t;
	int ret;
	if( ti == NULL ) {
		ret =  pthread_mutex_lock(fl);
		assert( ret == 0 );
	} else if ( ti->micro_sec == 0 && ti->sec == 0 ) {
		 return pthread_mutex_trylock(fl) == 0 ? true : false;
	} else {
		t.tv_sec =  ti->sec;
		t.tv_nsec = ti->micro_sec*1000;
		return pthread_mutex_timedlock(fl,&t) == 0 ? true : false;
	}
}

EXPORT_FUNC void thread_fastlock_release(fastlock* fl) {
	int ret = pthread_mutex_unlock(fl);
	assert( ret == 0 );
}

EXPORT_FUNC void thread_spinlock_create( spinlock* sp ) {
	int ret = pthread_spin_init(sp,0);
	assert( ret == 0 );
}

EXPORT_FUNC void thread_spinlock_destroy( spinlock* sp ) {
	int ret = pthread_spin_destroy(sp) ;
	assert( ret  == 0);
}

EXPORT_FUNC bool thread_spinlock_require( spinlock* sp  , bool once ) {
	int ret;
	if ( once == true ) {
		return pthread_spin_trylock(sp) == 0 ? true : false;
	} else {
		ret = pthread_spin_lock(sp) ;
		assert( ret == 0 );
	}
}

EXPORT_FUNC void thread_spinlock_release( spinlock* sp ) {
	int ret = pthread_spin_unlock(sp);
	assert( ret == 0 );
}


EXPORT_FUNC void thread_event_create( event* eve ) {
	int ret = pthread_cond_init(eve,NULL) ;
	assert( ret == 0 );
}

EXPORT_FUNC void thread_event_destroy( event* eve ) {
	int ret = pthread_cond_destroy(eve)  ;
	assert( ret == 0 );
}

EXPORT_FUNC bool thread_event_wait( event* eve , fastlock* fl , const struct th_join_sec* ti ) {
	struct timespec t;
	int ret;
	if( ti == NULL ) {
		ret = pthread_cond_wait(eve,fl);
		assert( ret == 0);
	} else {
		t.tv_nsec = ti->micro_sec * 1000 ;
		t.tv_sec = ti->sec ;
		ret = pthread_cond_timedwait(eve , fl , &t );
		if ( ret == ETIMEDOUT ) {
			return false;
		} else if ( ret == 0 ) {
			return true;
		} else {
			assert(0);
		}
	}
}


EXPORT_FUNC void thread_event_signal( event* eve ) {
	int ret = pthread_cond_signal( eve );
	assert( ret == 0 );
}

EXPORT_FUNC bool thread_event_broadcast( event* eve ) {
	int ret = pthread_cond_broadcast(eve) ;
	assert( ret = 0 );
}




EXPORT_FUNC void thread_tls_create( tls* t ) {
	int ret = pthread_key_create(t,NULL);
	assert( ret == 0 );
}

EXPORT_FUNC void thread_tls_destroy( tls t ) {
	int ret = pthread_key_delete(t);
	assert( ret == 0 );
}

EXPORT_FUNC void thread_tls_set( tls t , void* var ) {
	int ret = pthread_setspecific(t,var) ;
	assert( ret == 0 );
}

EXPORT_FUNC void thread_tls_get( tls t , void** varp ) {
	*varp = pthread_getspecific(t);
}
C_CODE_END
#endif //__linux__



